/**
 * 文件说明:多线程为相机处理过程,子线程进行数据处理
 */
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <vector>

// 共享队列、锁和条件变量
std::queue<std::vector<char>> dataQueue;
std::mutex mtx;
std::condition_variable cv;
bool finished = false; // 结束标志

//串口处理
void configureSerialPort(int fd, int baudRate, int dataBits, int stopBits, char parity);

// 父进程信号处理函数
void handle_data(int sig) {
    if (shmaddr->data_ready == 1) {
        printf("父进程：收到子进程信号，读取共享内存数据 -> %s\n", shmaddr->buffer);
        shmaddr->data_ready = 0;  // 清空标志位
    }
}

int main() {

    //初始化串口并获取文件描述符保存到fd变量里
    if ((serialPort = open("/dev/serial0", O_RDWR | O_NOCTTY | O_NDELAY |O_NONBLOCK)) < 0) //允许读写,非终端控制进程,非阻塞
    {
     fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno));
     return 1 ;
    }
    
    
    tcflush(serialPort, TCIFLUSH);
 
    // 设置串口参数
    configureSerialPort(serialPort, B115200, 8, 1, 'N');
    /********************************************************/
    
    /********************************************************/
    //设置共享内存
    int shmid;
    pid_t pid;
    key_t key = ftok(".", 'a'); // 生成唯一 key

    // 创建共享内存
    shmid = shmget(key, sizeof(SharedMemory), IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget failed");
        exit(1);
    }

    // 连接共享内存
    shmaddr = (SharedMemory *)shmat(shmid, NULL, 0);
    if (shmaddr == (SharedMemory *)-1) {
        perror("shmat failed");
        exit(1);
    }
    shmaddr->data_ready = 0; // 初始状态：无数据
    /********************************************************/
    
    /********************************************************/
    // 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork failed");
        exit(1);
    } else if (pid == 0) {  // 子进程：接收串口数据
        

        printf("子进程：监听串口数据...\n");
        while (1) {
            char buffer[256];
            int bytes_read = read(serial_fd, buffer, sizeof(buffer) - 1);
            if (bytes_read > 0) {
                buffer[bytes_read] = '\0';  // 添加字符串结束符
                printf("子进程：收到串口数据：%s\n", buffer);

                // 写入共享内存
                strncpy(shmaddr->buffer, buffer, SHM_SIZE);
                shmaddr->data_ready = 1;  // 标志位设为1，通知父进程

                // 发送信号通知父进程
                kill(getppid(), SIGUSR1);
            }
            usleep(100000);  // 100ms 轮询，避免 CPU 过高
        }

        close(serial_fd);
        shmdt(shmaddr);
    } else {  // 父进程
        printf("父进程：执行其他任务...\n");

        // 注册 SIGUSR1 信号处理函数
        signal(SIGUSR1, handle_data);

        while (1) {
            printf("父进程：正在执行其他任务...\n");
            sleep(3);  // 模拟父进程做其他任务
        }

        // 清理共享内存
        shmdt(shmaddr);
        shmctl(shmid, IPC_RMID, NULL);
    }

    return 0;
}



























void configureSerialPort(int fd, int baudRate, int dataBits, int stopBits, char parity) {
    struct termios options;
    
    /*
        struct termios
        {
          unsigned short c_iflag;      //输入模式标志
          unsigned short c_oflag;      //输出模式标志
          unsigned short c_cflag;      //控制模式标志
          unsigned short c_lflag;      //本地模式标志
          unsigned char c_line;        //线路规则
          unsigned char c_cc[NCC];     //控制字
        };
        c_iflag字段:IGNPAR 忽略帧错误和奇偶校验错误 INPCK
    */

    // 获取当前串口的设置
    if (tcgetattr(fd, &options) != 0) {
        std::cerr << "Error getting serial port attributes" << std::endl;
        return;
    }
    //不忽略换行和回车
    //options.c_iflag &=~INLCR;
    //options.c_iflag &=~IGNCR;
    //options.c_iflag &=~ICRNL;
    options.c_iflag &= ~(ICRNL | INLCR | IGNCR);//ICRNL 映射输入上的 CR 到 NL;INLCR 将 NL 映射到输入上的 CR;IGNCR 忽略回车符
    

    // 设置波特率
    cfsetispeed(&options, baudRate);
    cfsetospeed(&options, baudRate);

    // 设置数据位（通常为 8 位）
    options.c_cflag &= ~CSIZE;  // 清除数据位设置
    switch (dataBits) {
        case 5: options.c_cflag |= CS5; break;
        case 6: options.c_cflag |= CS6; break;
        case 7: options.c_cflag |= CS7; break;
        case 8: options.c_cflag |= CS8; break;
        default:
            std::cerr << "Invalid data bits" << std::endl;
            return;
    }

    // 设置停止位
    if (stopBits == 1) {
        options.c_cflag &= ~CSTOPB;  // 1 位停止位
    } else if (stopBits == 2) {
        options.c_cflag |= CSTOPB;   // 2 位停止位
    } else {
        std::cerr << "Invalid stop bits" << std::endl;
        return;
    }

    // 设置校验位
    if (parity == 'N') {
        options.c_cflag &= ~PARENB;  // 无校验
        options.c_iflag &= ~INPCK;   // 不启用输入校验
    } else if (parity == 'O') {
        options.c_cflag |= PARENB;   // 开启校验
        options.c_cflag |= PARODD;   // 奇校验
        options.c_iflag |= INPCK;    // 启用输入校验
    } else if (parity == 'E') {
        options.c_cflag |= PARENB;   // 开启校验
        options.c_cflag &= ~PARODD;  // 偶校验
        options.c_iflag |= INPCK;    // 启用输入校验
    } else {
        std::cerr << "Invalid parity option" << std::endl;
        return;
    }

    // 使能串口配置并关闭硬件流控制
    options.c_cflag |= (CLOCAL | CREAD);  // 启用接收并忽略调制解调器控制线
    options.c_cflag &= ~CRTSCTS;          // 禁用硬件流控制
    options.c_iflag &= ~(IXON | IXOFF | IXANY);// 禁用软件流控制
    //options.c_iflag |= IXON | IXOFF | IXANY;
    //软件流控制:options.c_iflag |= IXON | IXOFF | IXANY

    // 设置串口为原始模式
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag &= ~OPOST;

    // 设置数据传输时的字符大小，接收超时等(no blocked模式无意义)
    options.c_cc[VMIN] = 0;    // 1,至少读取 1 字节数据
    options.c_cc[VTIME] = 0;   // 5,超时 0.5 秒

    // 应用配置
    if (tcsetattr(fd, TCSANOW, &options) != 0) {
        std::cerr << "Error setting serial port attributes" << std::endl;
        return;
    }
}